﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ERP.ERPSystem.OrderManagement.Dto;
using ERP.Team.OrderManagement;
using ERP.Team.ProductionSystem;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Uow;

namespace ERP.ERPSystem.OrderManagement
{
    public class SalesOrderServices : ERPAppService, ISalesOrderServices
    {
        private readonly IRepository<SalesOrderMainModel, int> _repository;
        private readonly IRepository<SalesOrderProductDetailModel, int> _detailRepository;
        private readonly IRepository<CustomerModel, int> _customerRepository;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IRepository<ProductInfoRelationModel, int> _productInfoRelationRepository;

        public SalesOrderServices(
            IRepository<SalesOrderMainModel, int> repository,
            IRepository<SalesOrderProductDetailModel, int> detailRepository,
            IRepository<CustomerModel, int> customerRepository,
            IUnitOfWorkManager unitOfWorkManager,
            IRepository<ProductInfoRelationModel, int> productInfoRelationRepository)
        {
            _repository = repository;
            _detailRepository = detailRepository;
            _customerRepository = customerRepository;
            _unitOfWorkManager = unitOfWorkManager;
            _productInfoRelationRepository = productInfoRelationRepository;
        }

        public async Task<List<SalesOrderMainModel>> GetListAsync()
        {
            return await _repository.GetListAsync();
        }

        public async Task<List<SalesOrderMainModel>> GetListAsync(SalesOrderQueryDto query)
        {
            return await _repository.GetListAsync(x => 
                (string.IsNullOrEmpty(query.OrderCode) || x.OrderCode.Contains(query.OrderCode)) &&
                (string.IsNullOrEmpty(query.OrderName) || x.OrderName.Contains(query.OrderName))
            );
        }

        public async Task<SalesOrderDto> GetByIdAsync(int id)
        {
            var order = await _repository.GetAsync(id);
            var details = await _detailRepository.GetListAsync(x => x.SalesOrderMainCode == order.OrderCode);

            var orderDto = ObjectMapper.Map<SalesOrderMainModel, SalesOrderDto>(order);
            orderDto.Products = ObjectMapper.Map<List<SalesOrderProductDetailModel>, List<SalesOrderProductDetailDto>>(details);

            return orderDto;
        }

        [UnitOfWork]
        public async Task<SalesOrderMainModel> CreateAsync(SalesOrderDto input)
        {
            using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
            {
                try
                {
                    // 创建主表记录
                    var order = ObjectMapper.Map<SalesOrderDto, SalesOrderMainModel>(input);
                    order.OrderCode = GenerateOrderCode(); // 生成订单编码
                    await _repository.InsertAsync(order);

                    // 创建明细表记录
                    if (input.Products != null && input.Products.Any())
                    {
                        foreach (var product in input.Products)
                        {
                            var detail = ObjectMapper.Map<SalesOrderProductDetailDto, SalesOrderProductDetailModel>(product);
                            detail.SalesOrderMainCode = order.OrderCode;
                            await _detailRepository.InsertAsync(detail);
                        }
                    }

                    await uow.CompleteAsync();
                    return order;
                }
                catch (Exception)
                {
                    await uow.RollbackAsync();
                    throw;
                }
            }
        }

        [UnitOfWork]
        public async Task<SalesOrderDto> UpdateAsync(SalesOrderDto input)
        {
            using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
            {
                try
                {
                    // 更新主表
                    var order = await _repository.GetAsync(input.Id);
                    ObjectMapper.Map(input, order);
                    await _repository.UpdateAsync(order);

                    // 删除原有明细
                    await _detailRepository.DeleteAsync(x => x.SalesOrderMainCode == order.OrderCode);

                    // 添加新的明细
                    if (input.Products != null && input.Products.Any())
                    {
                        foreach (var product in input.Products)
                        {
                            var detail = ObjectMapper.Map<SalesOrderProductDetailDto, SalesOrderProductDetailModel>(product);
                            detail.SalesOrderMainCode = order.OrderCode;
                            await _detailRepository.InsertAsync(detail);
                        }
                    }

                    await uow.CompleteAsync();
                    return input;
                }
                catch (Exception)
                {
                    await uow.RollbackAsync();
                    throw;
                }
            }
        }

        [UnitOfWork]
        public async Task DeleteAsync(int id)
        {
            using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
            {
                try
                {
                    var order = await _repository.GetAsync(id);
                    
                    // 先删除明细
                    await _detailRepository.DeleteAsync(x => x.SalesOrderMainCode == order.OrderCode);
                    
                    // 再删除主表
                    await _repository.DeleteAsync(id);
                    
                    await uow.CompleteAsync();
                }
                catch (Exception)
                {
                    await uow.RollbackAsync();
                    throw;
                }
            }
        }

        public string GenerateOrderCode()
        {
            return "DD" + DateTime.Now.ToString("HHmmss") + new Random().Next(100, 999).ToString();
        }

        public async Task<List<CustomerModel>> GetCustomerListAsync()
        {
            return await _customerRepository.GetListAsync();
        }

        public async Task<List<ProductInfoRelationDto>> GetProductListAsync()
        {
            var products = await _productInfoRelationRepository.GetListAsync();
            return ObjectMapper.Map<List<ProductInfoRelationModel>, List<ProductInfoRelationDto>>(products);
        }
    }

    /// <summary>
    /// 雪花算法实现
    /// </summary>
    public class IdWorker
    {
        private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        private const long Twepoch = 1288834974657L;
        private const int WorkerIdBits = 5;
        private const int DatacenterIdBits = 5;
        private const int SequenceBits = 12;
        private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
        private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
        private const long SequenceMask = -1L ^ (-1L << SequenceBits);
        private const int WorkerIdShift = SequenceBits;
        private const int DatacenterIdShift = SequenceBits + WorkerIdBits;
        private const int TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
        private long _sequence = 0L;
        private long _lastTimestamp = -1L;

        public IdWorker(long workerId, long datacenterId, long sequence = 0L)
        {
            if (workerId > MaxWorkerId || workerId < 0)
                throw new ArgumentException($"worker Id can't be greater than {MaxWorkerId} or less than 0");

            if (datacenterId > MaxDatacenterId || datacenterId < 0)
                throw new ArgumentException($"datacenter Id can't be greater than {MaxDatacenterId} or less than 0");

            WorkerId = workerId;
            DatacenterId = datacenterId;
            _sequence = sequence;
        }

        private readonly object _lock = new object();
        public long WorkerId { get; protected set; }
        public long DatacenterId { get; protected set; }

        public long NextId()
        {
            lock (_lock)
            {
                var timestamp = TimeGen();
                if (timestamp < _lastTimestamp)
                    throw new Exception($"Clock moved backwards. Refusing to generate id for {_lastTimestamp - timestamp} milliseconds");

                if (_lastTimestamp == timestamp)
                {
                    _sequence = (_sequence + 1) & SequenceMask;
                    if (_sequence == 0)
                        timestamp = TilNextMillis(_lastTimestamp);
                }
                else
                {
                    _sequence = 0;
                }

                _lastTimestamp = timestamp;
                return ((timestamp - Twepoch) << TimestampLeftShift) |
                       (DatacenterId << DatacenterIdShift) |
                       (WorkerId << WorkerIdShift) |
                       _sequence;
            }
        }

        private long TilNextMillis(long lastTimestamp)
        {
            var timestamp = TimeGen();
            while (timestamp <= lastTimestamp)
                timestamp = TimeGen();
            return timestamp;
        }

        private long TimeGen()
        {
            return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
        }
    }
}
